package com.hp.snap.gr.db.voltdb.sp;

import com.hp.snap.gr.db.voltdb.sp.VoltDBTable.VoltDBRow;
import com.hp.snap.gr.exception.DatabaseException;
import com.hp.snap.gr.model.DBSystemInfo;

import org.apache.commons.lang.ArrayUtils;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class CommandResponses {
    private static final Logger logger = LoggerFactory.getLogger(CommandResponses.class);

    public static JSONObject getCommandResponse(JSONObject response) throws DatabaseException {
        if (logger.isTraceEnabled()) {
            logger.trace("The response:{}", response.toString());
        }
        try {
            int status = response.getInt("status");
            if (status < 0) {
                String errString = response.getString("statusstring");
                throw new DatabaseException("Call stored procedure failed for '{" + errString + "}' please check voltDB server status and your GR settings.");
            }
            JSONArray results = response.getJSONArray("results");
            if (results.length() < 1) {
                logger.warn("cannot get correct systeminfo response by querying voltdb");
                if (logger.isDebugEnabled()) {
                    logger.debug("[getDBSystemInfoData] query db response={}", response);
                }
            }
            JSONObject voltdbTable = results.getJSONObject(0);
            if (logger.isTraceEnabled()) {
                traceVoltDBTable(voltdbTable);
            }

            return voltdbTable;

        } catch (JSONException e) {
            throw new DatabaseException("Decode VoltDB Table (Admin Command) failed!", e);
        }
    }

    public static VoltDBTable getQuerySystemInfoTableData(JSONObject resultData) throws DatabaseException {
        VoltDBTable result = new VoltDBTable("SystemInfoResponse");
        try {
            JSONArray schema = resultData.getJSONArray("schema");
            List<String> schemaNames = new ArrayList<String>(schema.length());
            for (int ii = 0, nn = schema.length(); ii < nn; ii++) {
                JSONObject schemaRow = schema.getJSONObject(ii);
                String name = schemaRow.getString("name");
                int type = schemaRow.getInt("type");
                schemaNames.add(name);
                result.putHeader(name, type);
            }

            JSONArray data = resultData.getJSONArray("data");
            for (int ii = 0, nn = data.length(); ii < nn; ii++) {
                JSONArray dataRow = data.getJSONArray(ii);
                VoltDBRow row = new VoltDBRow();
                for (int jj = 0, mm = dataRow.length(); jj < mm; jj++) {
                    String name = schemaNames.get(jj);
                    String value = String.valueOf(dataRow.get(jj));
                    row.put(name, value);
                }
                result.addRow(row);
            }
        } catch (JSONException e) {
            throw new DatabaseException("Decode replication streams failed!", e);
        }
        return result;
    }

    public static DBSystemInfo getDBSystemInformation(JSONObject response) throws DatabaseException {
        JSONObject dbSysInfoJsonData = getCommandResponse(response);
        VoltDBTable dbSysInfoData = getQuerySystemInfoTableData(dbSysInfoJsonData);
        DBSystemInfo dbSysInfo = new DBSystemInfo();
        List<VoltDBRow> dbSysInfoRows = dbSysInfoData.getRows();
        Field[] fields = DBSystemInfo.class.getDeclaredFields();
        Method[] methods = retrieveAllMethods(DBSystemInfo.class);
        for (VoltDBRow row : dbSysInfoRows) {
            for (Field field : fields) {
                if (field.getName().equalsIgnoreCase((String) row.get("KEY"))) {
                    try {
/*                        field.setAccessible(true);
                        field.set(dbSysInfo, row.get("VALUE"));*/                        
                        Method method = getSpecificSetMethod(methods, getSpecificSetMethodName(field.getName()));
                        method.invoke(dbSysInfo, row.get("VALUE"));
                    } catch (IllegalArgumentException e) {
                        throw new DatabaseException(e.getMessage());
                    } catch (IllegalAccessException e) {
                        throw new DatabaseException(e.getMessage());
                    } catch (InvocationTargetException e) {
                        throw new DatabaseException(e.getMessage());
                    }
                }
            }
        }
        return dbSysInfo;

    }

    public static void traceVoltDBTable(JSONObject voltdbTable)
            throws DatabaseException {
        try {
            JSONArray schema = voltdbTable.getJSONArray("schema");
            List<String> schemaNames = new ArrayList<String>(schema.length());
            for (int ii = 0, nn = schema.length(); ii < nn; ii++) {
                JSONObject schemaRow = schema.getJSONObject(ii);
                schemaNames.add(schemaRow.getString("name"));
            }

            JSONArray data = voltdbTable.getJSONArray("data");
            for (int ii = 0, nn = data.length(); ii < nn; ii++) {
                JSONArray dataRow = data.getJSONArray(ii);
                StringBuilder sb = new StringBuilder();
                sb.append("Row[").append(ii).append("]: ");
                for (int jj = 0, mm = dataRow.length(); jj < mm; jj++) {
                    String column = String.valueOf(dataRow.get(jj));
                    String name = schemaNames.get(jj);
                    sb.append(name).append("=").append(column).append(", ");
                }
                if (dataRow.length() > 0) {
                    //QXCR1001397452, deleted extra chars
                    sb.delete(sb.length() - 1, sb.length());
                }
                logger.trace(sb.toString());
            }
        } catch (JSONException e) {
            throw new DatabaseException("Decode VoltDB Table failed!", e);
        }
    }
    
    public static Method getSpecificSetMethod(Method[] methods, String methodName) {
        Method method = null;
        for(Method method1: methods){
            if(method1.getName().equals(methodName)) {
                method = method1;
                break;
            }
        }
        return method;
    }
    public static Method[] retrieveAllMethods(Class clazz) {
        Method[] methods = clazz.getDeclaredMethods();

        Class currentClazz = clazz;
        while (currentClazz.getSuperclass() != null && currentClazz.getSuperclass() != Object.class) {
            Method[] superMethods = currentClazz.getSuperclass().getDeclaredMethods();

            methods = (Method[]) ArrayUtils.addAll(methods, superMethods);

            currentClazz = currentClazz.getSuperclass();
        }

        return methods;
    }
    private static String getSpecificSetMethodName(String fieldName) {
        String methodName = "";
        if (fieldName != null && !fieldName.isEmpty()){
            StringBuilder fieldNameWithFirstUpperCase = new StringBuilder().append(Character.toUpperCase(fieldName.charAt(0))).append(fieldName.substring(1));
            methodName = "set" + fieldNameWithFirstUpperCase;

        }
        return methodName;
    }
}
